import axios from 'axios'
import router from '@/router/index'
import md5 from 'md5'
import type { AxiosRequestConfig, Method, InternalAxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
import { useUserInfo } from '@/stores/modules/user'
import { isArray, isObject } from './is'

const pendingMap = new Map()
const APP_ID = '12312321'

/**
 * 根据运行环境获取基础请求URL
 */
export const getUrl = () => {
    const value = import.meta.env.VITE_AXIOS_BASE_URL
    return value == 'getCurrentDomain' ? window.location.protocol + '//' + window.location.host : value
}

/**
 * 根据运行环境获取基础请求URL的端口
 */
export const getUrlPort = () => {
    const url = getUrl()
    return new URL(url).port
}

/**
 * 创建`Axios`
 * 默认开启`reductDataFormat(简洁响应)`,返回类型为`ApiPromise`
 * 关闭`reductDataFormat`,返回类型则为`AxiosPromise`
 */
function createAxios<Data = any, T = ApiPromise<Data>>(axiosConfig: AxiosRequestConfig, options: Options = {}): T {
    const userInfo = useUserInfo()

    const Axios = axios.create({
        baseURL: getUrl(),
        timeout: 1000 * 60,
        headers: {
            'Content-Type': 'application/json',
        },
        responseType: 'json',
    })

    options = Object.assign(
        {
            CancelDuplicateRequest: true, // 是否开启取消重复请求, 默认为 true
            reductDataFormat: true, // 是否开启简洁的数据结构响应, 默认为true
            showErrorMessage: true, // 是否开启接口错误信息展示,默认为true
            showCodeMessage: true, // 是否开启code不为00000时的信息提示, 默认为true
        },
        options
    )

    // 请求拦截
    Axios.interceptors.request.use(
        (config) => {
            removePending(config)
            options.CancelDuplicateRequest && addPending(config)

            // 自动携带token
            if (config.headers) {
                const userToken = userInfo.state.token
                if (userToken) config.headers['Authorization'] = userToken
            }

            // 处理接口签名相关参数
            handleParamsSign(config)

            return config
        },
        (error) => {
            return Promise.reject(error)
        }
    )

    // 响应拦截
    Axios.interceptors.response.use(
        (response) => {
            removePending(response.config)

            if (response.config.responseType == 'json') {
                if (response.data && response.data.code !== '00000') {
                    if (options.showCodeMessage) {
                        ElMessage({
                            type: 'error',
                            message: response.data.message,
                        })
                    }

                    if (['A1001', 'A1002', 'A1001_1', 'A1006'].indexOf(response.data.code) >= 0) {
                        // token失效，需要重新登录
                        userInfo.removeToken()
                        // router.push({ name: 'Login' })
                    }

                    // code不等于00000, 页面then内的具体逻辑就不执行了
                    return Promise.reject(response.data)
                }
            }

            return options.reductDataFormat ? response.data : response
        },
        (error) => {
            error.config && removePending(error.config)
            options.showErrorMessage && httpErrorStatusHandle(error) // 处理错误状态码
            return Promise.reject(error) // 错误继续返回给到具体页面
        }
    )
    return Axios(axiosConfig) as T
}

export default createAxios

/**
 * 处理异常
 * @param {*} error
 */
function httpErrorStatusHandle(error: any) {
    // 处理被取消的请求
    if (axios.isCancel(error)) return console.error('因为请求重复被自动取消:' + error.message)
    let message = ''
    if (error && error.response) {
        switch (error.response.status) {
            case 302:
                message = $t('global.request.err302')
                break
            case 400:
                message = $t('global.request.err400')
                break
            case 401:
                message = $t('global.request.err401')
                break
            case 403:
                message = $t('global.request.err401')
                break
            case 404:
                message = $t('global.request.err404') + error.response.config.url
                break
            case 408:
                message = $t('global.request.timeout')
                break
            case 409:
                message = $t('global.request.err409')
                break
            case 500:
                message = $t('global.request.err500')
                break
            case 501:
                message = $t('global.request.err501')
                break
            case 502:
                message = $t('global.request.err502')
                break
            case 503:
                message = $t('global.request.err503')
                break
            case 504:
                message = $t('global.request.err504')
                break
            case 505:
                message = $t('global.request.err505')
                break
            default:
                message = $t('global.request.exception')
                break
        }
    }
    if (error.message.includes('timeout')) message = $t('global.request.timeout')
    if (error.message.includes('Network')) message = $t('global.request.networkErr')
    ElMessage({
        type: 'error',
        message: message ? message : error,
    })
}

/**
 * 储存每个请求的唯一cancel回调, 以此为标识
 */
function addPending(config: AxiosRequestConfig) {
    const pendingKey = getPendingKey(config)
    config.cancelToken =
        config.cancelToken ||
        new axios.CancelToken((cancel) => {
            if (!pendingMap.has(pendingKey)) {
                pendingMap.set(pendingKey, cancel)
            }
        })
}

/**
 * 删除重复的请求
 */
function removePending(config: AxiosRequestConfig) {
    const pendingKey = getPendingKey(config)
    if (pendingMap.has(pendingKey)) {
        const cancelToken = pendingMap.get(pendingKey)
        cancelToken(pendingKey)
        pendingMap.delete(pendingKey)
    }
}

/**
 * 生成每个请求的唯一key
 */
function getPendingKey(config: AxiosRequestConfig) {
    let { data } = config
    const { url, method, params, headers } = config
    if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象
    return [url, method, headers && headers['Authorization'] ? headers['Authorization'] : '', JSON.stringify(params), JSON.stringify(data)].join('&')
}

/**
 * 根据请求方法组装请求数据/参数
 */
export function requestPayload(method: Method, data: anyObj) {
    if (method == 'GET') {
        return {
            params: data,
        }
    } else if (method == 'POST') {
        return {
            data: data,
        }
    }
}

/**
 * 处理接口参数签名
 * @param config 接口请求数据
 */
function handleParamsSign(config: InternalAxiosRequestConfig) {
    const params = config.data || config.params
    const timestamp = new Date().getTime()
    const signParams = isObject(params) ? params : {}
    config.headers.sign = getSign({
        ...signParams,
        timestamp,
    })
    config.headers.appId = APP_ID
    config.headers.timestamp = timestamp
}

/**
 * 获取接口参数签名
 * @param params 接口参数
 * @param appSecret app密钥
 * @returns
 */
export function getSign(params: anyObj) {
    let signContext = ''
    const keys = Object.keys(params)
    keys.sort()
    for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const value = params[key]
        const emptyList = [undefined, null, '']
        if (emptyList.includes(value)) continue

        let tempVal = value
        if (isObject(value) || isArray(value)) {
            // 对象、数组数据类型转换成json字符串
            // tempVal = JSON.stringify(value, (key, val) => {
            //     // 过滤掉值为空的数据
            //     if (emptyList.includes(val)) return
            //     return val
            // })

            // 对象数组数据类型，赋值为时间戳
            tempVal = params.timestamp
        }
        if (signContext.length > 0) {
            signContext += `&${key}=${tempVal}`
        } else {
            signContext = `${key}=${tempVal}`
        }
    }
    signContext += import.meta.env.VITE_APP_SECRET
    // console.log('signContext---', signContext)

    // 生成签名
    const sign = md5(signContext).toUpperCase()
    return sign
}

export interface Options {
    // 是否开启取消重复请求, 默认为 true
    CancelDuplicateRequest?: boolean
    // 是否开启简洁的数据结构响应, 默认为true
    reductDataFormat?: boolean
    // 是否开启接口错误信息展示,默认为true
    showErrorMessage?: boolean
    // 是否开启code不为00000时的信息提示, 默认为true
    showCodeMessage?: boolean
}
